package cs3343.project.family.UI;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

import cs3343.project.family.CardController;
import cs3343.project.family.Character;
import cs3343.project.family.CharacterController;
import cs3343.project.family.ConfigManager;
import cs3343.project.family.GameController;
import cs3343.project.family.MapArea;
import cs3343.project.family.MapController;
import cs3343.project.family.ProfileManager;
import cs3343.project.family.RoleController;
import cs3343.project.family.StatisticsController;

/**
 * The Class GameFrame.
 */
public class GameFrame extends JFrame {
	
	/** The gc. */
	private GameController gc;
	
	/** The character controller. */
	private CharacterController characterController;
	
	/** The card controller. */
	private CardController cardController;
	
	/** The map controller. */
	private MapController mapController;
	
	/** The role controller. */
	private RoleController roleController;
	
	/** The statistics controller. */
	private StatisticsController statisticsController;
	
	/** The timer. */
	private Timer timer;

	/** The content pane. */
	private JPanel contentPane;
	
	/** The map panel. */
	private JPanel mapPanel = new JPanel();
	
	/** The player panel. */
	private JPanel playerPanel = new JPanel();
	
	/** The es panel. */
	private JPanel esPanel = new JPanel();
	
	/** The lower panel. */
	private JPanel lowerPanel = new JPanel();
	
	/** The status panel. */
	private JPanel statusPanel = new JPanel();
	
	/** The role panel. */
	private JPanel rolePanel = new JPanel();
	
	/** The card panel. */
	private JPanel cardPanel = new JPanel();
	
	/** The cur round. */
	private JLabel curRound;
	
	/** The cur round num. */
	private JLabel curRoundNum;
	
	/** The cur play. */
	private JLabel curPlay;
	
	/** The cur esteem. */
	private JLabel curEsteem;
	
	/** The cur index. */
	private JLabel curIndex;
	
	/** The cur role. */
	private JLabel curRole;
	
	/** The cur role des. */
	private JLabel curRoleDes;
	
	/** The cur card dock. */
	private JComboBox<String> curCardDock;
	
	/** The btn_ role function. */
	private JButton btn_RoleFunction;
	
	/** The dice button. */
	private JButton diceButton;
	
	/** The change char button. */
	private JButton changeCharButton;
	
	/** The imgs. */
	private ImageIcon[] imgs = new ImageIcon[6];
	
	/** The gimgs. */
	private ImageIcon[] gimgs = new ImageIcon[6];
	
	/** The display img. */
	private JLabel[] displayImg = new JLabel[6];
	
	/** The icons. */
	private JLabel[] icons = new JLabel[6];
	
	/** The names. */
	private JLabel[] names = new JLabel[6];
	
	/** The esteems. */
	private JLabel[] esteems = new JLabel[6];
		
	/** The map areas. */
	private MapArea[] mapAreas = new MapArea[40];
	
	/** The map img. */
	private JLabel[] mapImg;
	
	/** The running time. */
	private int runningTime;
	
	//for center player panel and dice image centering
	/** The game panel width. */
	private int gamePanelWidth;
	
	/** The game panel height. */
	private int gamePanelHeight;

	/**
	 * Create the frame.
	 *
	 * @param w the w
	 * @param h the h
	 * @param gc the gc
	 */
	public GameFrame(int w, int h, final GameController gc) {
		setResizable(false);
		
		this.gc = gc;
		characterController = this.gc.getCharacterController();
		cardController = this.gc.getCardController();
		mapController = this.gc.getMapController();
		roleController = this.gc.getRoleController();
		statisticsController = this.gc.getStatisticsController();
		timer = new Timer();
		runningTime = 0;
		
		statisticsController.initPlayerEsteemHistory(characterController.getNumOfPlayer());
		statisticsController.initPlayerGameLog(characterController.getNumOfPlayer());
		
		initUI(w, h);
		mapImg = new JLabel[40];
		
		setGamePanelWidth(w - 200);
		setGamePanelHeight(h - 200);
		
		//map
		initMapAreas();
		buildMap();
		
		//import card, role
		cardController.importAllCard();
		roleController.importRole();
		roleController.assignAllRole();		
		
		//init action button
		initDice();
		changeCharButton = new JButton(ConfigManager.getLangString("btnRoleExchange"));
		changeCharButton.setEnabled(false);

		//setup background
		mapPanel.setBackground(Color.LIGHT_GRAY);
		playerPanel.setBackground(Color.DARK_GRAY);
		esPanel.setBackground(Color.DARK_GRAY);
		lowerPanel.setBackground(Color.WHITE);
	
    	//setup round counter
	    curRound = new JLabel(ConfigManager.getLangString("lblRound"));
	    curRoundNum = new JLabel("0");
	    curRound.setBounds(getGamePanelWidth() / 2 - 100 - 20, 120, 200, 200);
	    curRoundNum.setBounds(getGamePanelWidth() / 2 - 100 + 60, 120, 200, 200);
	    curRound.setFont(new Font("Dialog", Font.BOLD, 20));
	    curRoundNum.setFont(new Font("Dialog", Font.BOLD, 20));
    	mapPanel.add(curRound);
    	mapPanel.add(curRoundNum);
		
	    //setup center and right player Icons
	    for (int i = 0; i < characterController.getNumOfPlayer(); i++) {
	    	imgs[i] = characterController.getPlayer(i).getImage();
	    	gimgs[i] = characterController.getPlayer(i).getGImage();
		    
		    icons[i] = new JLabel(characterController.getPlayer(i).getIcon());
		    names[i] = new JLabel(characterController.getPlayer(i).getName());
		    esteems[i] = new JLabel(String.valueOf(characterController.getPlayer(i).getEsteem()));
		    
		    if (i == 0)
		    	displayImg[i] = new JLabel(imgs[i]);
		    else
		    	displayImg[i] = new JLabel(gimgs[i]);
		    
		    playerPanel.add(displayImg[i]);
	    }
	    
	    for (int i = characterController.getNumOfPlayer(); i < ProfileManager.getProfileSize() - 1; i++)
	    	playerPanel.add(new JLabel(" "));

	    //setup right player panel
	    esPanel.setLayout(new GridLayout(2, 3));
	    playerPanel.setLayout(new GridLayout(7, 1));
    	playerPanel.add(diceButton);
    	
    	//setup center user self-esteem board
	    for (int i = 0; i < characterController.getNumOfPlayer(); i++) {	    	
	    	names[i].setBounds(getGamePanelWidth() / 2 - 100 - 20, 160 + 30 * i, 200, 200);
	    	esteems[i].setBounds(getGamePanelWidth() / 2 - 100 + 60, 160 + 30 * i, 200, 200);
	    	names[i].setFont(new Font("Dialog", Font.PLAIN, 20));
	    	esteems[i].setFont(new Font("Dialog", Font.PLAIN, 20));
	    	
	    	icons[i].setBounds(0, 0, 100, 100);
	    	mapPanel.add(names[i]); 
	    	mapPanel.add(esteems[i]); 
	    	mapPanel.add(icons[i]);
	    }
	    
	    //show status
	    statusPanel.setLayout(new GridLayout(1,4));
	  	curPlay = new JLabel(" " , JLabel.CENTER);
	  	curEsteem = new JLabel(" ", JLabel.CENTER);
		curIndex = new JLabel(" ", JLabel.CENTER);
		curRole = new JLabel(" ", JLabel.CENTER);
		
		//setup status panel
		statusPanel.add(curPlay);
		statusPanel.add(curEsteem);
		statusPanel.add(curIndex);
		statusPanel.add(curRole);
	    Font f = new Font("Dialog", Font.PLAIN, 20);
	    curPlay.setFont(f);
	    curEsteem.setFont(f);
	    curIndex.setFont(f);
	    curRole.setFont(f);
	    
	    //setup role description
		curRoleDes = new JLabel(" ");
		curRoleDes.setFont(new Font("Dialog", Font.PLAIN, 18));
		btn_RoleFunction = new JButton(ConfigManager.getLangString("btnRoleFunction"));
		btn_RoleFunction.setVisible(false);
		
	    rolePanel.setLayout(new BorderLayout());
	    rolePanel.add(curRoleDes, BorderLayout.CENTER);
	    rolePanel.add(btn_RoleFunction, BorderLayout.EAST);
	    
	    //setup card dock
	    cardPanel.setLayout(new BorderLayout());
	    curCardDock = new JComboBox<String>();
	    curCardDock.setFont(f);
	    curCardDock.addItem(" ");	
	    cardPanel.add(curCardDock, BorderLayout.CENTER);
	    cardPanel.add(changeCharButton, BorderLayout.EAST);
	    
	    //add border to lower panel
	    statusPanel.setBorder(new LineBorder(Color.LIGHT_GRAY));
	    rolePanel.setBorder(new LineBorder(Color.LIGHT_GRAY));
	    
	    //add all item into lower panel
		mapPanel.setLayout(null);
		lowerPanel.setLayout(new BorderLayout());
		lowerPanel.add(statusPanel, BorderLayout.NORTH);
		lowerPanel.add(rolePanel, BorderLayout.CENTER);
	    lowerPanel.add(cardPanel, BorderLayout.SOUTH);
	    
	    //game timer
	    timer.schedule(new TimerTask() {  
            @Override  
            public void run() {                 
                gc.setRunningTime(++runningTime);
            }
        }, 0, 1000);  
	 	  	
		diceButton.addActionListener(new ActionListener() {
			  public void actionPerformed(ActionEvent e) {
				  gc.rollDice();
			  }
		});
		
		btn_RoleFunction.addActionListener(new ActionListener() {
			  public void actionPerformed(ActionEvent e) {
				  statisticsController.addplayerLog(characterController.getCurrentPlayer(), 
						  ConfigManager.getLangString("btnRoleFunction") + 
						  " " + characterController.getCurrentPlayer().getRole().getEffectName());
				  gc.getRoleController().applyCurrentRoleFunction();
				  setPlayerPanel();
			  }
		});
		
		changeCharButton.addActionListener(new ActionListener() {
			  public void actionPerformed(ActionEvent e) {
				  statisticsController.addplayerLog(characterController.getCurrentPlayer(), ConfigManager.getLangString("roleExchange"));
				  Character beExchangedPlayer = gc.selectCharacter(ConfigManager.getLangString("msgChoosePlayerTitle"), ConfigManager.getLangString("msgChoosePlayer"));  
				  gc.exchangeCharacter(characterController.getCurrentPlayer(), beExchangedPlayer);
				  setPlayerPanel();
			  }
		});
	    
	    contentPane.add(playerPanel, BorderLayout.EAST);
	    contentPane.add(mapPanel, BorderLayout.CENTER);
	    contentPane.add(lowerPanel, BorderLayout.SOUTH);
	    
	    gc.getUiController().frameCentering(this);
	}
	
	/**
	 * Inits the ui.
	 *
	 * @param w the w
	 * @param h the h
	 */
	private void initUI(int w, int h) {
		setTitle(ConfigManager.getLangString("gameFrameTitle"));
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(300, 20, w, h);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(0, 0, 0, 0));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
	}
	
	/**
	 * Inits the dice.
	 */
	private void initDice() {		
		//setup center dice image
		JLabel[] diceImg = new JLabel[7];
		for (int i = 0; i < 7; i++) {
			diceImg[i] = gc.getDice().getDiceImage(i);
			diceImg[i].setBounds(getGamePanelWidth() / 2 + 20, 250, 100, 100);
			diceImg[i].setVisible(false);
		}
		
		//setup rightmost dice button
		ImageIcon img_dice = new ImageIcon(ConfigManager.IMAGE_PATH + "start.jpg");
	    diceButton = new JButton(img_dice);
	    
	    for (int i = 0; i < 7; i++) {
			mapPanel.add(gc.getDice().getDiceImage(i));
		}		
		gc.getDice().showDiceImage(0);
	}
	
	/**
	 * Show player image.
	 *
	 * @param index the index
	 */
	public void showPlayerImage(int index) {
		displayImg[index].setIcon(imgs[index]);
	}
	
	/**
	 * Hide player image.
	 *
	 * @param index the index
	 */
	public void hidePlayerImage(int index) {
		displayImg[index].setIcon(gimgs[index]);
	}
	
	/**
	 * Sets the player esteem label.
	 *
	 * @param player the new player esteem label
	 */
	public void setPlayerEsteemLabel(int player) {
		esteems[player].setText(String.valueOf(characterController.getPlayer(player).getEsteem()));
	}
	
	/**
	 * First set player icon location.
	 *
	 * @param player the player
	 */
	public void firstSetPlayerIconLocation(int player) {
		int x = 0, y = 0;
		
		if (characterController.getNumOfPlayer() < 6) {
			switch (player) {
				case 0: icons[player].setBounds(x, y, 50, 50); break;
				case 1: icons[player].setBounds(x + 50, y, 50, 50); break;
				case 2: icons[player].setBounds(x, y + 50, 50, 50); break;
				case 3: icons[player].setBounds(x + 50, y + 50, 50, 50); break;
				case 4: icons[player].setBounds(x, y, 100, 100); break;
			}
		}
		else {
			switch (player) {
				case 0: icons[player].setBounds(x, y, 50, 50); break;
				case 1: icons[player].setBounds(x + 50, y, 50, 50); break;
				case 2: icons[player].setBounds(x, y + 50, 50, 50); break;
				case 3: icons[player].setBounds(x + 50, y + 50, 50, 50); break;
				case 4: icons[player].setBounds(x, y, 50, 100); break;
				case 5: icons[player].setBounds(x, y, 150, 100); break;
			}
		}
		
		mapPanel.setComponentZOrder(icons[player], 0);
	}
	
	/**
	 * Sets the player icon location.
	 *
	 * @param player the new player icon location
	 */
	public void setPlayerIconLocation(int player) {
		int x = mapAreas[characterController.getPlayer(player).getMapAreaId()].getX();
		int y = mapAreas[characterController.getPlayer(player).getMapAreaId()].getY();
		
		if (characterController.getNumOfPlayer() < ConfigManager.MAX_PLAYER) {
			switch (hasPlayerCollision(player)) {
				case 1: icons[player].setBounds(x + 50, y, 50, 50); break;
				case 2: icons[player].setBounds(x, y + 50, 50, 50); break;
				case 3: icons[player].setBounds(x + 50, y + 50, 50, 50); break;
				case 4: icons[player].setBounds(x, y, 100, 100); break;
				default: icons[player].setBounds(x, y, 50, 50); break;
			}
		}
		else {
			switch (hasPlayerCollision(player)) {
				case 1: icons[player].setBounds(x + 50, y, 50, 50); break;
				case 2: icons[player].setBounds(x, y + 50, 50, 50); break;
				case 3: icons[player].setBounds(x + 50, y + 50, 50, 50); break;
				case 4: icons[player].setBounds(x, y, 50, 100); break;
				case 5: icons[player].setBounds(x, y, 150, 100); break;
				default: icons[player].setBounds(x, y, 50, 50); break;
			}
		}
		mapPanel.setComponentZOrder(icons[player], 0);
	}
	
	/**
	 * Checks for player collision.
	 *
	 * @param player the player
	 * @return the int
	 */
	public int hasPlayerCollision(int player) {
		int counter = 0;
		for (int i = 0; i < characterController.getNumOfPlayer(); i++)
			if (characterController.getPlayer(i).getMapAreaId() == 
				characterController.getPlayer(player).getMapAreaId())
				counter++;
		
		return counter - 1;
	}
	
	/**
	 * Sets the current player esteem label.
	 *
	 * @param player the new current player esteem label
	 */
	public void setCurrentPlayerEsteemLabel(int player) {
		curEsteem.setText(ConfigManager.getLangString("lblSE") + String.valueOf(characterController.getPlayer(player).getEsteem()));
	}
	
	/**
	 * Sets the player name label.
	 *
	 * @param player the new player name label
	 */
	public void setCurrentPlayerNameLabel(int player) {
		curPlay.setText(ConfigManager.getLangString("lblName") + String.valueOf(characterController.getPlayer(player).getName()));
	}
	
	/**
	 * Sets the player location label.
	 *
	 * @param player the new player location label
	 */
	public void setCurrentPlayerLocationLabel(int player) {
		curIndex.setText(ConfigManager.getLangString("lblMapArea") + String.valueOf(characterController.getPlayer(player).getMapAreaId()));
	}
	
	/**
	 * Sets the player role label.
	 *
	 * @param player the new player role label
	 */
	public void setCurrentPlayerRoleLabel(int player) {
		curRole.setText(ConfigManager.getLangString("lblRole") + 
						roleController.identifyRoleName(characterController.getPlayer(player).getRole().getRoleName()));
	}
	
	/**
	 * Sets the player role description label.
	 *
	 * @param player the new player role description label
	 */
	public void setPlayerRoleDescriptionLabel(int player) {
		curRoleDes.setText("<" + characterController.getPlayer(player).getRole().getEffectName() + "> " + 
							characterController.getPlayer(player).getRole().getDescription());
	}
	
	/**
	 * Sets the round label.
	 *
	 * @param roundNum the new round label
	 */
	public void setRoundLabel(int roundNum) {
		curRoundNum.setText(String.valueOf(roundNum));
	}
	
	/**
	 * Adds the player all owned cards in combo box.
	 *
	 * @param player the player
	 */
	public void addPlayerAllOwnedCardsInComboBox(int player) {
		if (characterController.getPlayer(player).getNoOfCardOwned() == 0)
			curCardDock.addItem(ConfigManager.getLangString("msgNoCardOption"));
		
		else {
			curCardDock.addItem(ConfigManager.getLangString("msgCardOption"));
			for (int i = 0; i < characterController.getPlayer(player).getNoOfCardOwned(); i++)
				curCardDock.addItem(characterController.getPlayer(player).getCardsOwned().get(i).getDescription());
		}
	}
	
	/**
	 * Removes the all cards in combo box.
	 */
	public void removeAllCardsInComboBox() {
		curCardDock.removeAllItems();
	}
	
	/**
	 * Enable change char button.
	 */
	public void enableChangeCharButton() {
		changeCharButton.setEnabled(true);
	}
	
	/**
	 * Disable change char button.
	 */
	public void disableChangeCharButton() {
		changeCharButton.setEnabled(false);
	}
	
	/**
	 * Inits the status bar.
	 */
	public void initStatusBar() {
		btn_RoleFunction.setVisible(true);
		curRoundNum.setText("1");
		diceButton.setIcon(new ImageIcon(ConfigManager.IMAGE_PATH + "dice.jpg"));
		setPlayerPanel();
	}
	
	/**
	 * Sets the player panel.
	 */
	public void setPlayerPanel() {
		//player panel image
		for (int i = 0; i < characterController.getNumOfPlayer(); i++) {
			if (gc.getCurrentPlayerIndex() == i) {
				showPlayerImage(i);
				setCurrentPlayerEsteemLabel(i);
				setCurrentPlayerNameLabel(i);
				setCurrentPlayerLocationLabel(i);
				setCurrentPlayerRoleLabel(i);
				setPlayerRoleDescriptionLabel(i);
			}			
			else
				hidePlayerImage(i);
			
			setPlayerEsteemLabel(i);
			
			//check if current player has exchange character card
			cardController.enableExchangeCharacter(characterController.getCurrentPlayer());
			
			//update card dock
			removeAllCardsInComboBox();
			addPlayerAllOwnedCardsInComboBox(i);
		}
	}
	
	/**
	 * Sets the player land.
	 *
	 * @param ran the ran
	 * @param character the character
	 */
	public void setPlayerLand(int ran, Character character) {
		if (character.getMapAreaId() < mapController.getMapSize()-1) {
			character.setMapAreaId(character.getMapAreaId() + ran);
		}
		else {
			character.setMapAreaId(character.getMapAreaId() + ran - mapController.getMapSize() - 1);
		}
		statisticsController.addplayerLog(character, "移動了" + ran + "步");
		setPlayerIconLocation(gc.getCurrentPlayerIndex());
	}
	
	/**
	 * Inits the map areas.
	 */
	private void initMapAreas() {			    
	    for (int i = 0; i < 40; i++) {
	    	mapAreas[i] = new MapArea(i, new ImageIcon(ConfigManager.IMAGE_PATH + "land.jpg"), 0, 0);
	    	mapImg[i] = new JLabel(mapAreas[i].getImage());
	    }
	}
	
	/**
	 * Builds the map.
	 */
	private void buildMap() {
		//draw mapAreas
	    int x = 0; 
	    int y = 0;
	    int k = 0;
	    
	    //--->
	    for (int i = 0; i < mapController.getMapDimension(); i++) {
	    	int imgW = mapAreas[k].getImage().getIconWidth();
	    	int imgH = mapAreas[k].getImage().getIconHeight();
	    	
	    	mapImg[k].setBounds(i * imgW, y, imgW, imgH);
	    	mapAreas[k].setX(i * imgW);
	    	mapAreas[k].setY(y);
	    	mapPanel.add(mapImg[k]);
			
			k++;
			x = i * imgW;
	    }
	    
	    //|
	    //|
	    //V
	    for (int i = 0; i < 5; i++) {
	    	int imgW = mapAreas[k].getImage().getIconWidth();
	    	int imgH = mapAreas[k].getImage().getIconHeight();
	    	
	    	mapImg[k].setBounds(x, imgH + i * imgH, imgW, imgH);
	    	mapAreas[k].setX(x);
	    	mapAreas[k].setY(imgH + i * imgH);
	    	mapPanel.add(mapImg[k]);
			
			k++;
			y = imgH + i * imgH;
	    }
	    
	    //<---
	    for (int i = 0; i < mapController.getMapDimension() - 1; i++) {
	    	int imgW = mapAreas[k].getImage().getIconWidth();
	    	int imgH = mapAreas[k].getImage().getIconHeight();
	    	
	    	mapImg[k].setBounds(x - imgW * i - imgW, y, imgW, imgH);
	    	mapAreas[k].setX(x - imgW * i - imgW);
	    	mapAreas[k].setY(y);
	    	mapPanel.add(mapImg[k]);
			
			k++;
	    }
	    
	    //^
	    //|
	    for (int i = 0; i < 5; i++) {
	    	int imgW = mapAreas[k].getImage().getIconWidth();
	    	int imgH = mapAreas[k].getImage().getIconHeight();
	    	
	    	mapImg[k].setBounds(0, y - imgH * i - imgH, imgW, imgH);
	    	mapAreas[k].setX(0);
	    	mapAreas[k].setY(y - imgH * i - imgH);
	    	mapPanel.add(mapImg[k]);
			
			k++;
	    }	    
	}
	
	/**
	 * Sets the role function button.
	 *
	 * @param str the str
	 * @param enable the enable
	 */
	public void setRoleFunctionButton(String str, boolean enable) {
		btn_RoleFunction.setText(str);
		btn_RoleFunction.setEnabled(enable);
	}

	/**
	 * Gets the game panel width.
	 *
	 * @return the game panel width
	 */
	public int getGamePanelWidth() {
		return gamePanelWidth;
	}

	/**
	 * Sets the game panel width.
	 *
	 * @param gamePanelWidth the new game panel width
	 */
	public void setGamePanelWidth(int gamePanelWidth) {
		this.gamePanelWidth = gamePanelWidth;
	}

	/**
	 * Gets the game panel height.
	 *
	 * @return the game panel height
	 */
	public int getGamePanelHeight() {
		return gamePanelHeight;
	}

	/**
	 * Sets the game panel height.
	 *
	 * @param gamePanelHeight the new game panel height
	 */
	public void setGamePanelHeight(int gamePanelHeight) {
		this.gamePanelHeight = gamePanelHeight;
	}
	
	/**
	 * Disable all button.
	 */
	public void disableAllButton() {
		btn_RoleFunction.setEnabled(false);
		diceButton.setEnabled(false);
		changeCharButton.setEnabled(false);
	}
	
	/**
	 * Enable dice button.
	 */
	public void enableDiceButton() {
		diceButton.setEnabled(true);
	}
}
